// =====================================================================================
// 
//       Filename:  poker.cpp
// 
//    Description:  
// 
//        Version:  1.0
//        Created:  26.01.2010 20:34:41
//       Revision:  none
//       Compiler:  g++
// 
//         Author:  Dmitriy Serdyuk (), serdyuk.dmitriy@gmail.com
//        Company:  
// 
// =====================================================================================
#include"poker.h"
#include "player.h"
#include <algorithm>
#include <sstream>
#include <map>





// =====================================================================================
//                                     Game
// =====================================================================================
Game::Game() : smallBlind(100), bigBlind(200){
    isStarted = false;
    myDiller = myPlayers.begin();

}

void Game::addPlayer(Player player){
    if(!isStarted){
        std::stringstream out;

        out << "pl" << myPlayers.size();

      

        //player.setName(out.str());
        myPlayers.push_back(player);
    }
}

std::vector<Player>::iterator Game::getNext(){
    if (myDiller == myPlayers.end() - 1) {
        return myPlayers.begin();
    } else {
        return (myDiller + 1);
    }
}

std::vector<Player>::iterator Game::getAfterNext(){
    if (myDiller == myPlayers.end() - 1) {
        return myPlayers.begin() + 1;
    }
    if (myDiller == myPlayers.end() - 2) {
        return myPlayers.begin();
    }
    return myDiller + 2;

}

void Game::makeRound(){
    if (myRound == preflop) {
        myPack.reset();
        myPack.shuffle();
        for (unsigned int i = 0; i < myPlayers.size(); ++i) {
            myPlayers[i].addCard(myPack.getCard());
            myPlayers[i].addCard(myPack.getCard());
        }

        std::vector<Player>::iterator next, afterNext;
        next = getNext();
        afterNext = getAfterNext();

        if ( smallBlind == next->getMoney(smallBlind) ){
            //TODO
        }
        if ( bigBlind == afterNext->getMoney(bigBlind) ){
            //TODO
        }

        while (/* condition */) {
            /* code */
        }
    }
    if (myRound == flop) {
        

    }
    if (myRound == turn) {
        

    }
    if (myRound == river) {
        

    }
}








bool Game::isRoyalFlush(Cards cards){ // is working

    for(int suit = 0; suit < NUM_OF_SUITS; ++suit){
        
        for (unsigned int i = 0; i < cards.size(); ++i) {
            if(cards[i].myRange == A && cards[i].mySuit == (SUITS)suit){
               
                for (unsigned int j = 0; j < cards.size(); ++j) {
                    if(cards[j].myRange == K && cards[j].mySuit == (SUITS)suit){
                        
                        for (unsigned int k = 0; k < cards.size(); ++k) {
                            if(cards[k].myRange == Q && cards[k].mySuit == (SUITS)suit){

                                for (unsigned int l = 0; l < cards.size(); ++l) {
                                    if(cards[l].myRange == J && cards[l].mySuit == (SUITS)suit){

                                        for (unsigned int m = 0; m < cards.size(); ++m) {
                                            if(cards[m].myRange == f10 && cards[m].mySuit == (SUITS)suit){

                                                return true;

                                            }
                                        }


                                    }
                                }


                            }
                        }


                    }
                }

            }
        }

    }

    return false;
}

Cards Game::getRoyalFlush(Cards cards){ // is working

    Cards temp;

    for(int suit = 0; suit < NUM_OF_SUITS; ++suit){
        temp.clear();
        
        for (unsigned int i = 0; i < cards.size(); ++i) {
            if(cards[i].myRange == A && cards[i].mySuit == (SUITS)suit){
                temp.push_back(cards[i]);
               
                for (unsigned int j = 0; j < cards.size(); ++j) {
                    if(cards[j].myRange == K && cards[j].mySuit == (SUITS)suit){
                        temp.push_back(cards[j]);
                        
                        for (unsigned int k = 0; k < cards.size(); ++k) {
                            if(cards[k].myRange == Q && cards[k].mySuit == (SUITS)suit){
                                temp.push_back(cards[k]);

                                for (unsigned int l = 0; l < cards.size(); ++l) {
                                    if(cards[l].myRange == J && cards[l].mySuit == (SUITS)suit){
                                        temp.push_back(cards[l]);

                                        for (unsigned int m = 0; m < cards.size(); ++m) {
                                            if(cards[m].myRange == f10 && cards[m].mySuit == (SUITS)suit){
                                                temp.push_back(cards[m]);

                                                return temp;

                                            }
                                        }


                                    }
                                }


                            }
                        }


                    }
                }

            }
        }

    }

    Cards c;
    return c;
}

bool Game::isStraightFlush(Cards cards){ // is working
    for(int suit; suit < NUM_OF_SUITS; ++suit){
        Cards t;
        for (unsigned int i = 0; i < cards.size(); ++i) {
            
            if( cards[i].mySuit == (SUITS)suit){
                t.push_back(cards[i]);
            }
        }
        if (t.size() >= 5) {
            if (isStraight(t)) {
                return true;
            }
        }

    }

    return false;
}

Cards Game::getStraightFlush(Cards cards){ // is working

    for(int suit; suit < NUM_OF_SUITS; ++suit){
        Cards t;
        for (unsigned int i = 0; i < cards.size(); ++i) {
            
            if( cards[i].mySuit == (SUITS)suit){
                t.push_back(cards[i]);
            }
        }
        if (Game::isStraight(t)) {
            return Game::getStraight(t);
        }

    }

    Cards c;
    return c;
}

bool Game::isQuads(Cards cards){ // is working

    Cards temp = cards;


    for(int range = 0; range < NUM_OF_RANGES; ++range){

        Cards t;
        for (unsigned int i = 0; i < temp.size(); ++i) {
            
            if( temp[i].myRange == (RANGES)range){
                t.push_back(temp[i]);
            }
        }
        if (t.size() >= 4) {
            return true;
        }

    }

    return false;
}


Cards Game::getQuads(Cards cards){ // is working
    for(int range = 0; range < NUM_OF_RANGES; ++range){
        Cards temp = cards;
        Cards t;
        Cards::iterator i = temp.begin();
        while (i != temp.end()) {
            if (i->myRange == (RANGES)range) {
                t.push_back(*i);
                temp.erase(i);
                i = temp.begin();
            } else {
                ++i;
            }
        }
        if (t.size() >= 4) {
            Cards out;
            for(int i = 0; i < 4; ++i){
                out.push_back(t[i]);
            }
            std::sort(temp.begin(), temp.end());
            out.push_back(temp[0]);
            return out;
        }

    }
    
    Cards c;
    return c;
}


bool Game::isFullHouse(Cards cards){  // is working

    for(int range = 0; range < NUM_OF_RANGES; ++range){
        Cards t;
        for (unsigned int i = 0; i < cards.size(); ++i) {
            
            if( cards[i].myRange == (RANGES)range){
                t.push_back(cards[i]);
            }
        }
        if (t.size() >= 3) {

            for(int range1 = 0; range1 < NUM_OF_RANGES; ++range1){
                Cards tt;
                for (unsigned int i = 0; i < cards.size(); ++i) {
            
                    
                    if( cards[i].myRange == (RANGES)range1){
                        tt.push_back(cards[i]);
                    }
                }
                if ((RANGES)range1 != t[0].myRange && tt.size() >= 2) {
                    return true;
                }

            }

        }

    }

    return false;
    
}

Cards Game::getFullHouse(Cards cards){ // is working


    for(int range = 0; range < NUM_OF_RANGES; ++range){
        Cards temp = cards;
        Cards t;
        Cards::iterator i = temp.begin();
        while (i != temp.end()) {
            if (i->myRange == (RANGES)range) {
                t.push_back(*i);
                temp.erase(i);
                i = temp.begin();
            } else {
                ++i;
            }
        }
        if (t.size() >= 3) {

            for(int range1 = 0; range1 < NUM_OF_RANGES; ++range1){
                Cards temp2 = temp;
                Cards tt;
                Cards::iterator i = temp2.begin();
                while (i != temp2.end()) {
                    if (i->myRange == (RANGES)range1) {
                        tt.push_back(*i);
                        temp2.erase(i);
                        i = temp2.begin();
                    } else {
                        ++i;
                    }
                }
                if ((RANGES)range1 != t[0].myRange && tt.size() >= 2) {
                    Cards out;
                    for (int i = 0; i < 3; ++i) {
                        out.push_back(t[i]);
                    }
                    for (int i = 0; i < 2; ++i) {
                        out.push_back(tt[i]);
                    }
                    std::sort(temp2.begin(), temp2.end());
                    //out.push_back(temp2[0]);
                    
                    return out;
                }

            }

        }

    }

    Cards c;
    return c;
}


bool Game::isFlush(Cards cards){  // is working

    for(int suit; suit < NUM_OF_SUITS; ++suit){
        Cards t;
        for (unsigned int i = 0; i < cards.size(); ++i) {
            
            if( cards[i].mySuit == (SUITS)suit){
                t.push_back(cards[i]);
            }
        }
        if (t.size() >= 5) {
            return true;
        }

    }

    return false;
}


Cards Game::getFlush(Cards cards){  // is working

    for(int suit; suit < NUM_OF_SUITS; ++suit){
        Cards cardsi = cards;
        Cards t;
        for (unsigned int i = 0; i < cardsi.size(); ++i) {
            
            if( cardsi[i].mySuit == (SUITS)suit){
                t.push_back(cardsi[i]);
            }
        }
        if (t.size() >= 5) {
            Cards temp;
            std::sort(t.begin(), t.end());
            for (int i = 0; i < 5; ++i) {
                temp.push_back(t[i]);
            }

            return temp;
        }

    }

    Cards c;
    return c;
}

bool Game::isStraight(Cards cards){ // is working

    Cards temp = cards;

    std::sort(temp.begin(), temp.end());
    std::vector<int> starts;
    std::vector<int> length;

    int len = 1;
    int start = 0;
    for (unsigned int i = 1; i < temp.size(); ++i) {
        
        if ((int)(temp[i - 1].myRange) == (int)(temp[i].myRange) - 1) {
            len += 1;
        } else {
            if (len >= 4) {
                starts.push_back(start);
                length.push_back(len);
            }
            len = 1;
            start = i;
        }

    }
    if (len >= 4) {
        starts.push_back(start);
        length.push_back(len);
    }
    for (unsigned int i = 0; i < starts.size(); ++i) {
        if (length[i] >= 5 || (length[i] == 4 && temp[starts[i] + length[i] - 1].myRange == f2 && temp[0].myRange == A)) {
            return true;
        }
    }
    
    return false;
}

Cards Game::getStraight(Cards cards){  // is working

    Cards temp = cards;

    std::sort(temp.begin(), temp.end());
    std::vector<int> starts;
    std::vector<int> length;

    int len = 1;
    int start = 0;
    for (unsigned int i = 1; i < temp.size(); ++i) {
        
        if ((int)(temp[i - 1].myRange) == (int)(temp[i].myRange) - 1) {
            len += 1;
        } else {
            if (len >= 4) {
                starts.push_back(start);
                length.push_back(len);
            }
            len = 1;
            start = i;
        }

    }
    if (len >= 4) {
        starts.push_back(start);
        length.push_back(len);
    }
    for (unsigned int i = 0; i < starts.size(); ++i) {
        if (length[i] == 4 && temp[starts[i] + length[i] - 1].myRange == f2 && temp[0].myRange == A) {
            Cards t;
            for (int j = starts[i]; j < starts[i] + 4; ++j) {
                t.push_back(temp[j]);
            }
            t.push_back(temp[0]);
            return t;
        }
        if (length[i] >= 5) {
            Cards t;
            for (int j = starts[i]; j < starts[i] + 5; ++j) {
                t.push_back(temp[j]);
            }
            return t;
        }
    }
    
    Cards c;
    return c;
}

bool Game::isSet(Cards cards){ // is working

    for(int range = 0; range < NUM_OF_RANGES; ++range){
        Cards t;
        for (unsigned int i = 0; i < cards.size(); ++i) {
            
            if( cards[i].myRange == (RANGES)range){
                t.push_back(cards[i]);
            }
        }
        if (t.size() >= 3) {
            return true;
        }

    }

    return false;
}

Cards Game::getSet(Cards cards){  // is working


    for(int range = 0; range < NUM_OF_RANGES; ++range){
        Cards temp = cards;
        Cards t;
        Cards::iterator i = temp.begin();
        while (i != temp.end()) {
            if (i->myRange == (RANGES)range) {
                t.push_back(*i);
                temp.erase(i);
                i = temp.begin();
            } else {
                ++i;
            }
        }
        std::sort(temp.begin(), temp.end());
        if (t.size() >= 3) {
            Cards tt;
            for (int i = 0; i < 3; ++i) {
                tt.push_back(t[i]);
            }
            tt.push_back(temp[0]);
            tt.push_back(temp[1]);
            return tt;
        }

    }

    Cards c;
    return c;
}


bool Game::isTwoPairs(Cards cards){ // is working

    for(int range = 0; range < NUM_OF_RANGES; ++range){
        Cards t;
        for (unsigned int i = 0; i < cards.size(); ++i) {
            
            if( cards[i].myRange == (RANGES)range){
                t.push_back(cards[i]);
            }
        }
        if (t.size() >= 2) {

            for(int range1 = 0; range1 < NUM_OF_RANGES; ++range1){
                Cards tt;
                for (unsigned int i = 0; i < cards.size(); ++i) {
            
                    
                    if( cards[i].myRange == (RANGES)range1){
                        tt.push_back(cards[i]);
                    }
                }
                if ((RANGES)range1 != t[0].myRange && tt.size() >= 2) {
                    return true;
                }

            }

        }

    }

    return false;
}

Cards Game::getTwoPairs(Cards cards){ // is working


    for(int range = 0; range < NUM_OF_RANGES; ++range){
        Cards temp = cards;
        Cards t;
        Cards::iterator i = temp.begin();
        while (i != temp.end()) {
            if (i->myRange == (RANGES)range) {
                t.push_back(*i);
                temp.erase(i);
                i = temp.begin();
            } else {
                ++i;
            }
        }
        if (t.size() >= 2) {

            for(int range1 = 0; range1 < NUM_OF_RANGES; ++range1){
                Cards temp2 = temp;
                Cards tt;
                Cards::iterator i = temp2.begin();
                while (i != temp2.end()) {
                    if (i->myRange == (RANGES)range1) {
                        tt.push_back(*i);
                        temp2.erase(i);
                        i = temp2.begin();
                    } else {
                        ++i;
                    }
                }
                if ((RANGES)range1 != t[0].myRange && tt.size() >= 2) {
                    Cards out;
                    for (int i = 0; i < 2; ++i) {
                        out.push_back(t[i]);
                    }
                    for (int i = 0; i < 2; ++i) {
                        out.push_back(tt[i]);
                    }
                    std::sort(temp2.begin(), temp2.end());
                    out.push_back(temp2[0]);
                    
                    return out;
                }

            }

        }

    }
    Cards c;
    return c;
}


bool Game::isOnePair(Cards cards){ // is working
    
    for(int range = 0; range < NUM_OF_RANGES; ++range){
        Cards t;
        for (unsigned int i = 0; i < cards.size(); ++i) {
            
            if( cards[i].myRange == (RANGES)range){
                t.push_back(cards[i]);
            }
        }
        if (t.size() >= 2) {
            return true;
        }

    }

    return false;
}


Cards Game::getOnePair(Cards cards){  //is working



    for(int range = 0; range < NUM_OF_RANGES; ++range){
        Cards temp = cards;
        Cards t;
        Cards::iterator i = temp.begin();
        while (i != temp.end()) {
            if (i->myRange == (RANGES)range) {
                t.push_back(*i);
                temp.erase(i);
                i = temp.begin();
            } else {
                ++i;
            }
        }
        if (t.size() >= 2) {
            Cards out;
            for(int i = 0; i < 2; ++i){
                out.push_back(t[i]);
            }
            std::sort(temp.begin(), temp.end());
            for(int i = 0; i < 3; ++i){
                out.push_back(temp[i]);
            }
            return out;
        }

    }
    Cards c;
    return c;
}

Cards Game::Kicker(Cards cards){
    Cards temp = cards;
    std::sort(temp.begin(), temp.end());

    Cards out;
    for(int i = 0; i < 5; ++i){
        out.push_back(temp[i]);
    }
    return out;
}

int Game::compareSameComb(Cards first, Cards second){

    Cards tfirst = first, tsecond = second;
    std::sort(tfirst.begin(), tfirst.end());
    std::sort(tsecond.begin(), tsecond.end());
    int i = 0;
    while (tfirst[i] == tsecond[i] && i < 5) {
        ++i;
    }
    if (tfirst[i] > tsecond[i]) {
        return 1;
    }
    if (tfirst[i] < tsecond[i]) {
        return -1;
    }
    if (tfirst[i] == tsecond[i]) {
        return 0;
    }
    return 0;
}

int Game::compare(Cards first, Cards second){ // return 1 if first > second, -1 if second > first, 0 if first == second
    //RoyalFlush
    if (isRoyalFlush(first)) {
        if (isRoyalFlush(second)) {
            return 0;
        } else {
            return 1;
        }
    }
    if (isRoyalFlush(second)) {
        return -1;
    }
    /////////////////////////


    //StraighFlush
    if (isStraightFlush(first)) {
        if (isStraightFlush(second)) {
            return compareSameComb(getStraightFlush(first), getStraightFlush(second));
        } else {
            return 1;
        }
    }
    if (isStraightFlush(second)) {
        return -1;
    }
    ////////////////////////


    //Quads
    if (isQuads(first)) {
        if (isQuads(second)) {
            return compareSameComb(getQuads(first), getQuads(second));
        } else {   
            return 1;
        }
    }
    if (isQuads(second)) {
        return -1;
    }
    /////////////////////////


    //FullHouse
    if (isFullHouse(first)) {
        if (isFullHouse(second)) {
            return compareSameComb(getFullHouse(first), getFullHouse(second));
        } else {
            return 1;
        }
    }
    if (isFullHouse(second)) {
        return -1;
    }
    ////////////////////////


    //Flush
    if (isFlush(first)) {
        if (isFlush(second)) {
            return compareSameComb(getFlush(first), getFlush(second));
        } else {
            return 1;
        }
    }
    if (isFlush(second)) {
        return -1;
    }
    //////////////////////


    //Straight
    if (isStraight(first)) {
        if (isStraight(second)) {
            return compareSameComb(getStraight(first), getStraight(second));
        } else {
            return 1;
        }
    }
    if (isStraight(second)) {
        return -1;
    }
    //////////////////////


    //Set
    if (isSet(first)) {
        if (isSet(second)) {
            return compareSameComb(getSet(first), getSet(second));
        } else {
            return 1;
        }
    } 
    if (isSet(second)) {
        return -1;
    }
    //////////////////////

    //TwoPairs
    if (isTwoPairs(first)) {
        if (isTwoPairs(second)) {
            return compareSameComb(getTwoPairs(first), getTwoPairs(second));
        } else {
            return 1;
        }
    } 
    if (isTwoPairs(second)) {
        return -1;
    }
    /////////////////////

    //OnePair
    if (isOnePair(first)) {
        return compareSameComb(getOnePair(first), getOnePair(second));
    } else {
        return 1;
    }
    if (isOnePair(second)) {
        return -1;
    }
    /////////////////////


    //Kicker
    return compareSameComb(Kicker(first), Kicker(second));
}
